import { Parser } from 'htmlparser2'
import { h } from './core'
import { parsehtml } from './ast'
import type { vnode, RenderFunction } from '../types'

function isEmpty(obj:unknown) {
  return ['{}', '[]'].includes(JSON.stringify(obj))
}

function camelCase() {}


function genProps(props:object) {
  if(isEmpty(props)) return {}
  const propsList: string[] = []

  Object.keys(props).forEach(name=>{
    if(name === 'style') {
      const value = props[name] as string
      props[name] = value.split(/;\s*/g).map(item=>item.split(/:\s*/g))
        .reduce((prev, [name, value])=>{
          prev[name] = value
          return prev
        }, {})
    }
  })

  return props
}

function genChildren(children:any) {
  return children.map((child:any)=>{
    if(typeof child === 'string') {
      const mustacheReg = /\{\{((?:.|\r?\n)+?)\}\}/g

      if(mustacheReg.test(child)) {
        let lastIndex = mustacheReg.lastIndex = 0
        const tokens:string[] = []

        let result: RegExpExecArray|null = null
      
        while(result = mustacheReg.exec(child)) {
          const { index } = result

          if(index > lastIndex) {
            tokens.push(`${JSON.stringify(child.slice(lastIndex, index))}`)
          }
          
          tokens.push(`_s(${result[1].trim()})`)

          lastIndex = index + result[0].length
        }

        if(lastIndex < child.length) {
          tokens.push(`${JSON.stringify(child.slice(lastIndex))}`)
        }

        return `_v(${tokens.join('+')})`
      } else {
        return `_v(${JSON.stringify(child)})`
      }
    }
    return generate(child)
  })
}


function generate(ast:any) {
  const props = JSON.stringify(genProps(ast.props))
  const children = genChildren(ast.children)

  const code = `_c(${JSON.stringify(ast.tag)}, ${props}, ${ isEmpty(children) ? '[]' : children})`

  return `${code}`
}

export function compileToFunction(tpl:string): RenderFunction {
  const ast = parsehtml(tpl.trim())
  const code = generate(ast)

  const render = new Function(`with(this) { return ${code} }`)
  
  return render as RenderFunction
}
